#include <hd.h>
#include <port.h>

#define ATA_INFO        0xEC            // 获取硬盘信息
#define ATA_READ        0x20            // 读取扇区数据
#define ATA_WRITE       0x30            // 数据写入扇区

// 硬盘 I/O 端口及寄存器定义
#define REG_DEV_CTRL    0x3F6
#define REG_DATA        0x1F0
#define REG_FEATURES    0x1F1
#define REG_ERROR       0x1F1
#define REG_NSECTOR     0x1F2
#define REG_LBA_LOW     0x1F3
#define REG_LBA_MID     0x1F4
#define REG_LBA_HIGH    0x1F5
#define REG_DEVICE      0x1F6
#define REG_STATUS      0x1F7
#define REG_COMMAND     0x1F7

// 硬盘状态位定义
#define	STATUS_BUSY     0x80
#define	STATUS_DRDY     0x40
#define	STATUS_DFSE     0x20
#define	STATUS_DSC      0x10
#define	STATUS_DRQ      0x08
#define	STATUS_CORR     0x04
#define	STATUS_IDX      0x02
#define	STATUS_ERR      0x01


/******************************************************************************
* 函数名称: void HardDiskInit(void)
* 功能说明: 硬盘初始化
* 输入参数: 无
* 输出参数: 无
* 函数返回: 无
* 其它说明: 无
******************************************************************************/
void HardDiskInit(void)
{
    
}

/******************************************************************************
* 函数名称: E_RET HardDiskRead(U32 sn, U08* buf)
* 功能说明: 读硬盘 sn 号扇区中的数据到 buf 中
* 输入参数: U32 sn      --扇区号
    　　　　U08* buf    --数据区
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 一次性读取一整个扇区 SEC_SIZE 字节数据
******************************************************************************/
E_RET HardDiskRead(U32 sn, U08* buf)
{
    WritePort(REG_NSECTOR, 1);                  // 设置要读取的扇区数，固定每次只读取一个扇区
    WritePort(REG_LBA_LOW, (sn&0xFF));          // 设置 LBA Low, 扇区号低 8 位
    WritePort(REG_LBA_MID, ((sn>>8)&0xFF));     // 设置 LBA Min, 扇区号中 8 位
    WritePort(REG_LBA_HIGH, ((sn>>16)&0xFF));   // 设置 LBA High, 扇区号高 8 位
    WritePort(REG_DEVICE, (((sn>>24)&0x0F) | 0xE0));   // 设置 Device,  bit4 ~ bit7 为 1110(0xE0)，表示 LBA 模式，低 4 位与LBA low Mid High 共拼成 28 位扇区地址寻址

    WritePort(REG_COMMAND, ATA_READ);           // 读扇区命令

    // 等待硬盘设备 Ready
    {volatile U32 i = 999; while(i--);}
    
    ReadPortW(REG_DATA, (U16 *)buf, SEC_SIZE>>1);

    return E_OK;
}

/******************************************************************************
* 函数名称: E_RET HardDiskWrite(U32 sn, U08* buf)
* 功能说明: 将 buf 中的数据写入到硬盘 sn 号扇区中
* 输入参数: U32 sn      --扇区号
    　　　　U08* buf    --数据区
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 一次性写入一整个扇区 SEC_SIZE 字节数据
******************************************************************************/
E_RET HardDiskWrite(U32 sn, U08* buf)
{
    WritePort(REG_NSECTOR, 1);                  // 设置要读取的扇区数，固定每次只读取一个扇区
    WritePort(REG_LBA_LOW, (sn&0xFF));          // 设置 LBA Low, 扇区号低 8 位
    WritePort(REG_LBA_MID, ((sn>>8)&0xFF));     // 设置 LBA Min, 扇区号中 8 位
    WritePort(REG_LBA_HIGH, ((sn>>16)&0xFF));   // 设置 LBA High, 扇区号高 8 位
    WritePort(REG_DEVICE, (((sn>>24)&0x0F) | 0xE0));   // 设置 Device,  bit4 ~ bit7 为 1110(0xE0)，表示 LBA 模式，低 4 位与LBA low Mid High 共拼成 28 位扇区地址寻址

    WritePort(REG_COMMAND, ATA_WRITE);          // 写扇区命令

    // 等待硬盘设备 Ready
    {volatile U32 i = 999; while(i--);}
    
    WritePortW(REG_DATA, (U16 *)buf, SEC_SIZE>>1);

    return E_OK;
}

